/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.travelFinder.service;

import com.travelFinder.repository.PlaceRepository;
import java.util.Date;
import org.neo4j.graphalgo.GraphAlgoFactory;
import org.neo4j.graphalgo.PathFinder;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.travelFinder.domain.Place;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.neo4j.graphdb.*;
import org.springframework.data.neo4j.support.Neo4jTemplate;

/**
 *
 * @author michau
 */
@Service
public class DbServiceTemporary {

    @Autowired
    PlaceRepository placeRepository;
	
	@Autowired 
	Neo4jTemplate template;

    public Iterable<Path> findShortestConnection(Date starting_date, Place start, Place end, int max_liczba_przesiadek) {
        return findShortestConnection(starting_date, start, end, max_liczba_przesiadek, 3);
    }
        
    public Iterable<Path> findShortestConnection(Date starting_date, Place start, Place end, int max_liczba_przesiadek, int ile_polaczen) {
        PathFinder<Path> finder = GraphAlgoFactory.allSimplePaths(new MyExpander(starting_date), 45);

		Logger.getLogger(DbPopulator.class.getName()).log(Level.INFO, "Count: " + placeRepository.count());	

		Logger.getLogger(DbPopulator.class.getName()).log(Level.INFO, "Start: " + start.getName());
		Logger.getLogger(DbPopulator.class.getName()).log(Level.INFO, "End: " + end.getName());
		
        Node nodeA = template.getPersistentState(start);
        Node nodeB = template.getPersistentState(end);
        Iterable<Path> paths = finder.findAllPaths( nodeA, nodeB );
        ArrayList<Path> list = new ArrayList<Path>();
        ArrayList<Integer> costs = new ArrayList<Integer>();
        int min_cost = 0;
        for (Path path : paths)
        {
            int cost = countPathDuration(path, starting_date, max_liczba_przesiadek);
            if(cost == Integer.MAX_VALUE)
            {
                continue;
            }
            if(list.size() < ile_polaczen)
            {
                min_cost = Math.max(cost, min_cost);
                list.add(path);
                costs.add(cost);
            }
            else if(cost < min_cost)
            {
                int idx = costs.indexOf(min_cost);
                list.remove(idx);
                costs.remove(idx);
                list.add(path);
                costs.add(cost);
                min_cost = Collections.max(costs);
            }
        }
        ArrayList<Path> retlist = new ArrayList<Path>();
        while(!list.isEmpty())
        {
            int idx = costs.indexOf(Collections.min(costs));
            retlist.add(list.get(idx));
            list.remove(idx);
            costs.remove(idx);
        }
        if(retlist.isEmpty())
        {
            return null;
        }
        return retlist;
    }
    
    /**
     * Oblicz czas podrozy
     */
    private int countPathDuration(Path p, Date starting_date, int przesiadki)
    {
        int DAY_IN_MSECS = 86400000;
        int INTERVAL = DAY_IN_MSECS;

        Relationship currentConnection;
        
        starting_date = new Date(starting_date.getTime() + 2*3600*1000);
        Date currentDate = (Date) starting_date.clone();

        String connId = null;
        ++przesiadki;
        
        Iterator<Relationship> i = p.relationships().iterator();
        while(i.hasNext())
        {
            currentConnection = i.next();
            String currConnId = currentConnection.getProperty("connectionId").toString();
            if(!currConnId.equals(connId))
            {
                --przesiadki;
                connId = currConnId;
            }
            if(przesiadki < 0)
            {
                return Integer.MAX_VALUE;
            }
            long currTime = currentDate.getTime()%DAY_IN_MSECS;
            long connTime = Integer.parseInt(currentConnection.getProperty("startTime").toString()) * 1000 * 60;
            
            //sprawdz godzine odjazdu
            if(currTime > connTime)
            {
                if(DAY_IN_MSECS - currTime + connTime > INTERVAL)
                {
                    return Integer.MAX_VALUE;
                }
                currentDate = new Date(currentDate.getTime() - currTime + DAY_IN_MSECS);
            }
            else
            {
                if(connTime - currTime > INTERVAL)
                {
                    return Integer.MAX_VALUE;
                }
                currentDate = new Date(currentDate.getTime() - currTime);
            }
            
            if(new Date(Long.parseLong(currentConnection.getProperty("startDate").toString())).after(currentDate))
            {
                currentDate = new Date(Long.parseLong(currentConnection.getProperty("startDate").toString()));
            }

            //sprawdz czy w aktywnym dniu
          /*  int currentDay = currentDate.getDay();
            --currentDay;
            if(currentDay<0) currentDay=6;
            Object obj = currentConnection.getProperty("weekdaysActive2");
            boolean[] active = (boolean[]) obj;
            
                while (!active[currentDay]) {
                    currentDay = (currentDay + 1) % 7;
                    currentDate = new Date(currentDate.getTime() + DAY_IN_MSECS);
                } 
            */
            /*
            if(!((Weekdays)currentConnection.getProperty("weekdaysActive")).isActive(currentDay))
            {
                currentDay = (currentDay + 1)%7;
                currentDate = new Date(currentDate.getTime() + DAY_IN_MSECS);
                while(!((Weekdays)currentConnection.getProperty("weekdaysActive")).isActive(currentDay))
                {
                    currentDay = (currentDay + 1)%7;
                    currentDate = new Date(currentDate.getTime() + DAY_IN_MSECS);
                }
            }*/

            //dodaj czas trwania podrozy
            int duration = Integer.parseInt(currentConnection.getProperty("duration").toString()) * 60;
            currentDate = new Date(currentDate.getTime() + connTime + duration * 1000);
        }

        return (int) ((currentDate.getTime() - starting_date.getTime())/1000);
    }
    
    class MyExpander implements RelationshipExpander
    {
        private final Date startDate;
        
        public MyExpander(Date startDate)
        {
            this.startDate = startDate;
        }

        @Override
        public Iterable<Relationship> expand(Node node)
        {
            int DAY_IN_MSECS = 86400000;
            Iterable<Relationship> tmp = node.getRelationships(Direction.OUTGOING);
            Iterator<Relationship> i = tmp.iterator(); 
            LinkedList<Relationship> list = new LinkedList<Relationship>();
            while(i.hasNext())
            {
                Relationship rel = i.next();
                if(!(new Date(Long.parseLong(rel.getProperty("endDate").toString())).before(startDate))
                        && !(new Date(Long.parseLong(rel.getProperty("startDate").toString()) + DAY_IN_MSECS).after(startDate)))
                {
                    list.add(rel);
                }
            }
            return list;
        }

        @Override
        public RelationshipExpander reversed() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        
    }
}
